#include <iostream>
#include <fstream>
#include <locale>
#include <string>

#include "BTree/BTree.h"
#include "ArchFrase/ArchFrases.h"
#include "ArchFrase/BloqueFrase.h"
#include "IndiceBooleano/IndiceBooleano.h"

#include "utils/StopWords.h"
#include "utils/Normalizador.h"
#include "utils/PalabraAleatoria.h"

#include "Menues.h"


using namespace std;

/** Forward declarations
 * */
bool cargarArchivoFrases(ArchFrases& archFrases, string nomOrig, string nomDest);
bool cargarArbolTerminos(BTree &arbol, string nomDiccionario, string nomStopWords, string nomArchArbol);


int main() {
	cout << "Orga datos TP-2 (MK-IV):\nEntrega final v10" << endl;

	// Las clases de datos
	BTree arbolTerminos;
	ArchFrases archFrases;
	IndiceBooleano idxBool;

	Operacion op = NADA;
	Menues menu(archFrases, idxBool);
	do {
		op = menu.principal();

		switch (op){
			case INICIALIZAR_ARCHIVOS:
				{
					// Carga el archivo de frases en bloques
					bool exito = cargarArchivoFrases(archFrases, "frases-utf8.txt", "bloqueFrases.bin");
					if (!exito) cout << "\t*** Error al cargar el archivo de frases ***\n" << endl;

					// Carga el árbol B+ de terminos
					exito = cargarArbolTerminos(arbolTerminos, "diccionarioTerminos.bin", "stop-words.txt", "terminosPermitidos.bin");
					if (!exito) cout << "\t*** Error al cargar el árbol de terminos ***\n" << endl;

					// Construye el indice booleano
					cout << "Creando el indice booleano...";
					exito = idxBool.crearIndices(archFrases, arbolTerminos);
					if (!exito) cout << "\n\t*** Error al crear el indice booleano ***\n" << endl;
					else cout << "listo!" << endl;

					// TODO: Construir el indice de porciones de firmas
				}
				break;

			case ABRIR_ARCHIVOS:
				{
					// Abre el archivo de frases en bloques
					cout << "Abriendo el archivo de frases...";
					archFrases.abrir("bloqueFrases.bin");
					cout << "listo!" << endl;

					// Abre el archivo de indice booleano, aparentemente los nombres son por defecto
					cout << "Abriendo el indice booleano..." << flush;
					bool exito = idxBool.abrirIndices();
					if (!exito) cout << "\n\t*** Error al abrir el indice booleano ***\n" << endl;
					else cout << "listo!" << endl;

					// Abre el de terminos validos, lo necesita para las altas y bajas
					cout << "Abriendo el archivo de terminos permitidos..." << flush;
					exito = arbolTerminos.abrir("terminosPermitidos.bin");
					if (!exito) cout << "\n\t*** Error al abrir el archivo de terminos permitidos ***\n" << endl;
					else cout << "listo!" << endl;
				}
				break;

			case CONSULTA_IDX_BOOLEANO:
				{
					// Carga los terminos para la busqueda
					std::vector<std::string> vecTerminos = menu.ingresarTerminosBusqueda();

					// Recupera los IDs de todos los documentos que respondan a los terminos
					vector<unsigned int> vecDocsIDs = idxBool.buscarDocumentos(vecTerminos);

					// Muestra los resultados de la buqueda
					menu.mostrarResultados(vecDocsIDs);
				}
				break;

			case ALTA_FRASE:
				{
					// Se guarda la nueva frase
					BloqueFrase bf = menu.ingresarNuevaFrase();
					int pos = archFrases.guardarFrase(bf);

					// Actualiza el indice booleano
					idxBool.altaDocumento(bf.getFrase(), pos, arbolTerminos);
					cout << "La frase fue dada de alta\n" << endl;
				}
				break;

			case BAJA_FRASE:
				{
					unsigned int docID = menu.ingresarIDFrase();
					if (docID == numeric_limits<unsigned int>::max()){
						cout << "\t*** El id no es valido ***" << endl;
						break;
					}

					BloqueFrase bf;
					archFrases.leerFrase(bf, docID);
					std::cout << "\nSe eliminara la frase:" << std::endl;
					std::cout << bf.getFrase() << "\n\t - " << bf.getAutor() << std::endl;

					// Elimina la frase del archivo de frases y actualiza el indice
					archFrases.borrarFrase(docID);
					idxBool.bajaDocumento(bf.getFrase(), docID, arbolTerminos);
					cout << "\nLa frase fue dada de baja\n" << endl;
				}
				break;

			case MODIFICAR_FRASE:
			case CONSULTA_IDX_FIRMAS:
				{
					cout << "*** No implementado! ***\n" << endl;
				}
				break;

			case NADA:
			case SALIR:
				break;
		}

	} while (op != SALIR);

	// Cierra las clases de datos
	idxBool.cerrar();
	archFrases.cerrar();
	arbolTerminos.cerrar();

	cout << "Fin." << endl;

	return 0;
}

//---------------------------------------------

bool cargarArchivoFrases(ArchFrases& archFrases, string nomOrig, string nomDest){
	// Los archivos de origen y destino
	wifstream fin(nomOrig.c_str());
	if (!fin) return false;

	archFrases.crear(nomDest);

	// Se le injecta el locale correspondiente
	locale loc("es_AR.UTF8");
	fin.imbue(loc);

	cout << "Creando archivo de frases en bloque...";

	// Se procesa el archivo por lineas
	while ( !fin.eof() ){
		wstring linea;
		getline(fin, linea);

		// Existe al menos una frase muy larga
		if ( !fin.eof() && linea.length() < 256 ){

			// Normaliza las lineas y las convierte en un bloque del archivo de frases
			string lineaNormal = Normalizador::normalizarLineaFrase(linea);
			BloqueFrase bf = Normalizador::parsearLineaFrase(lineaNormal);

			// Guarda la frase en el archivo
			archFrases.guardarFrase(bf);
		}
	}

	cout << "listo!" << endl;

	// Cierra el archivo de origen
	fin.close();

	return true;
}

//-----------------------------------------------

// Carga el arbol de terminos sin las stop-words
bool cargarArbolTerminos(BTree &arbol, string nomDiccionario, string nomStopWords, string nomArchArbol){
	ifstream fin(nomDiccionario.c_str(), ifstream::in | ifstream::binary);
	if (!fin) return false;

	cout << "Cargando archivo de stop-words...";

	// Permite verificar si la palabra a cargar es o no una stop-word
	StopWords stopWords(nomStopWords);
	bool exito = stopWords.init();
	if (!exito) return false;

	cout << "listo!" << endl;
	cout << "Cargando árbol de terminos..." << flush;

	arbol.crear(nomArchArbol);
	do {
		PalabraAleatoria p;
		p.cargarPalabra(fin);

		if (!fin.eof()){
			if ( !stopWords.es_stopWord(p.getPalabra()) ){

				RegistroDatos* reg = new RegistroDatos(p.getPalabra(), fin.tellg());
				Resultado res = arbol.insertarRegistro(reg);
				if (res == CLAVE_DUPLICADA) delete reg;
			}
		}

	} while (!fin.eof());
	fin.close();

	cout << "listo!" << endl;

	return true;
}

